home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (C) 1994, Silicon Graphics, Inc.
- * All Rights Reserved.
- *
- * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
- * the contents of this file may not be disclosed to third parties, copied or
- * duplicated in any form, in whole or in part, without the prior written
- * permission of Silicon Graphics, Inc.
- *
- * RESTRICTED RIGHTS LEGEND:
- * Use, duplication or disclosure by the Government is subject to restrictions
- * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
- * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
- * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
- * rights reserved under the Copyright Laws of the United States.
- */
- #include <string.h>
- #include "sw.h"
- #include "extern.h"
- #include "ship.h"
- #include "main.h"
- #include "control.h"
- #include "universe.h"
- #include "sw_comm.h"
- #include "sound.h"
- #include <Inventor/nodes/SoSeparator.h>
- #include <Inventor/nodes/SoTranslation.h>
- #include <Inventor/nodes/SoRotation.h>
- #include <Inventor/nodes/SoTransform.h>
- #include <Inventor/nodes/SoSwitch.h>
- #include <Inventor/nodes/SoMaterial.h>
- #include <Inventor/nodes/SoGroup.h>
- #include <Inventor/nodes/SoScale.h>
- #include <Inventor/nodes/SoCoordinate3.h>
- #include <Inventor/nodes/SoMaterialBinding.h>
- #include <Inventor/nodes/SoLightModel.h>
- #include <Inventor/nodes/SoComplexity.h>
- #include <Inventor/nodes/SoSphere.h>
- #include <Inventor/nodes/SoCylinder.h>
- #include <Inventor/nodes/SoCone.h>
- #include <Inventor/nodes/SoTriangleStripSet.h>
- #include <Inventor/nodes/SoQuadMesh.h>
- #include <Inventor/actions/SoGetBoundingBoxAction.h>
-
-
- #define DEFAULT_MASS 50000 // kg
- #define DEFAULT_ANGACCEL (M_PI/3.0) // rad/s/s
- #define DEFAULT_RESPONSE 0.6 // frac / second
- #define DEFAULT_MAXSHIELD 100000 // Joules
- #define DEFAULT_SHIELDRECOVERY 0 /*2000*/ // J/s
- #define DEFAULT_LASER_POWER 30000 // J/s
- #define DEFAULT_MISSILE_ENERGY 35000 // J
- #define DEFAULT_MAXMISSILES 20
- #define DEFAULT_MAXFORCE (100.0*50000.0) // N
- #define DEFAULT_FUELRATE 0.01
-
- #define MAXTRACKACC (M_PI/3.0) // rad/s
- #define ANGVELMULT 3.0
- #define DISSIPATE_RATE 1.0 // shield shade dissipate rate
- #define MISSILE_EXPLODE 1.5 // missile explosion time
- #define SHIP_EXPLODE 4.0 // ship explode time
- #define SOLARTHRUST 0.04
- #define MISSILE_ACCEL 196.0
- #define MISSILE_ANG (M_PI/2.0)
- #define MISSILE_TIME (12.0 + MISSILE_EXPLODE)
- #define MISSILE_SIZE 3.0
- #define MISSILE_MASS 1000
- #define GLIDE_TIME 1.2
-
- #define SHIELDSIZE 1.75
- #define HITSPREAD (M_PI/3.0)
- #define HITRES 2
- #define HITSIZE (2*HITRES+1)
- #define HITPOINTS (HITSIZE * HITSIZE)
-
- static SbRotation zeroAngularVelocity = SbRotation::identity();
- static SbVec3f straightAhead(0.0, 0.0, -1.0);
-
- //
- // ShipObject
- //
-
- SoSeparator* ShipObject::missileGeom = NULL;
-
- ShipObject::ShipObject(NetId id, Team team, const char* name)
- {
- wholeShip = new SoSeparator;
- wholeShip->ref();
- wholeShip->addChild(activeSwitch = new SoSwitch);
-
- // set constants
- info.myId = id;
- myTeam = team;
- myName = strdup(name);
-
- // initialize score
- info.won = 0;
- info.lost = 0;
-
- // ship is not active yet
- info.info.active = ObjectInactive;
- activeSwitch->whichChild = SO_SWITCH_NONE; // hide all
- }
-
- ShipObject::~ShipObject()
- {
- wholeShip->unref();
- }
-
- void ShipObject::init(SoSeparator* shipGeom,
- SoSeparator* shipChangeGeom,
- const SbVec3f& turretPos,
- const SbVec3f& flagPos,
- const SbVec3f& cockpitPos)
- {
- cockpit = cockpitPos;
-
- // make laser turret stuff
- laserLocation = new SoTranslation;
- laserOrientation = new SoRotation;
- laserLocation->translation.setValue(turretPos);
-
- // make missile
- if (missileGeom == NULL) {
- missileGeom = new SoSeparator;
- missileGeom->ref();
- missileGeom->setGLRenderCaching(TRUE); // missiles don't change
-
- SoComplexity* missileCmplx = new SoComplexity;
- missileGeom->addChild(missileCmplx);
- missileCmplx->value = 0.2;
- SoRotation* missileTurn = new SoRotation;
- missileGeom->addChild(missileTurn);
- missileTurn->rotation.setValue(SbRotation(SbVec3f(0.0, 1.0, 0.0),
- SbVec3f(0.0, 0.0, -1.0)));
-
- SoSeparator* nose = new SoSeparator;
- nose->setGLRenderCaching(TRUE);
- missileGeom->addChild(nose);
- SoTranslation* nosePos = new SoTranslation;
- nose->addChild(nosePos);
- nosePos->translation.setValue(0.0, 3.0, 0.0);
- SoCone* noseGeom = new SoCone;
- nose->addChild(noseGeom);
- noseGeom->height = 1.0;
- noseGeom->bottomRadius = 0.5;
- noseGeom->parts = SoCone::SIDES;
-
- SoSeparator* body = new SoSeparator;
- body->setGLRenderCaching(TRUE);
- missileGeom->addChild(body);
- SoCylinder* bodyGeom = new SoCylinder;
- body->addChild(bodyGeom);
- bodyGeom->height = 5.0;
- bodyGeom->radius = 0.5;
- bodyGeom->parts = SoCylinder::SIDES | SoCylinder::BOTTOM;
-
- SoSeparator* engine = new SoSeparator;
- engine->setGLRenderCaching(TRUE);
- missileGeom->addChild(engine);
- SoMaterial* engineMat = new SoMaterial;
- engine->addChild(engineMat);
- engineMat->ambientColor.setValue(0.0, 0.0, 0.0);
- engineMat->diffuseColor.setValue(0.0, 0.0, 0.0);
- engineMat->specularColor.setValue(0.0, 0.0, 0.0);
- engineMat->emissiveColor.setValue(0.0, 1.0, 1.0);
- engineMat->shininess = 0.0;
- engineMat->transparency = 0.5;
- SoTranslation* enginePos = new SoTranslation;
- engine->addChild(enginePos);
- enginePos->translation.setValue(0.0, -3.0, 0.0);
- SoCone* engineGeom = new SoCone;
- engine->addChild(engineGeom);
- engineGeom->height = 2.0;
- engineGeom->bottomRadius = 1.0;
- engineGeom->parts = SoCone::SIDES;
- }
-
- // make separator under active switch
- SoSeparator* activeShip = new SoSeparator;
- activeSwitch->addChild(activeShip);
-
- // ship stuff (i.e. all except missiles) under shipParts
- SoSeparator* shipParts = new SoSeparator;
- activeShip->addChild(shipParts);
- shipParts->addChild(shipPosition = new SoTranslation);
- shipParts->addChild(shipOrientation = new SoRotation);
-
- // put spacecraft geometry under switch
- shipParts->addChild(shipSwitch = new SoSwitch);
- SoGroup* shipGroup = new SoGroup;
- shipSwitch->addChild(shipGroup);
-
- // add static ship parts (under switch)
- shipGroup->addChild(shipGeom);
- shipGeom->setGLRenderCaching(TRUE); // cache static ship nodes
-
- // add dynamic ship parts (under switch)
- shipGroup->addChild(shipChangeGeom);
- shipGroup->addChild(makeLaserTurret());
-
- // add laser beam under its own switch
- shipParts->addChild(laserSwitch = new SoSwitch);
- laserSwitch->addChild(makeLaserBeam());
-
- // add flag scene with a switch to choose which one to show
- SoSeparator* flagSep = new SoSeparator;
- shipParts->addChild(flagSep);
- SoTranslation* flagLoc = new SoTranslation;
- flagSep->addChild(flagLoc);
- flagLoc->translation.setValue(flagPos);
- flagPosition = flagPos;
- flagSep->addChild(flagSwitch = new SoSwitch);
- for (int i = 0; i < NUMTEAMS; i++)
- flagSwitch->addChild(flagGeometry(Team(i)));
- flagSwitch->whichChild = SO_SWITCH_NONE;
-
- // get ship size
- SoGetBoundingBoxAction bboxAction;
- bboxAction.apply(shipGeom);
- SbVec3f c = bboxAction.getBoundingBox().getCenter();
- sx = c[0];
- sy = c[1];
- sz = c[2];
- bboxAction.getBoundingBox().getSize(sdx, sdy, sdz);
- // sdx *= 0.5;
- // sdy *= 0.5;
- // sdz *= 0.5;
- sbs = (sdx > sdy) ? sdx : sdy;
- if (sdz > sbs) sbs = sdz;
- sbs *= SHIELDSIZE;
-
- // add shield hits geometry
- shipParts->addChild(makeShields());
-
- // make missiles
- SoSeparator* allMissiles = new SoSeparator;
- activeShip->addChild(allMissiles);
-
- for (i = 0; i < MAXMISSILES; i++) {
- allMissiles->addChild(missileSwitch[i] = new SoSwitch);
- SoSeparator* missileStuff = new SoSeparator;
- missileSwitch[i]->addChild(missileStuff);
- missileStuff->addChild(missilePosition[i] = new SoTranslation);
- missileStuff->addChild(missileOrientation[i] = new SoRotation);
- missileStuff->addChild(missileGeom);
- }
-
- // get missile size
- bboxAction.apply(missileGeom);
- bboxAction.getBoundingBox().getSize(mdx, mdy, mdz);
- mdx *= 0.5;
- mdy *= 0.5;
- mdz *= 0.5;
- mbs = (mdx > mdy) ? mdx : mdy;
- if (mdz > mbs) mbs = mdz;
- }
-
- Team ShipObject::team() const
- {
- return myTeam;
- }
-
- char* ShipObject::name() const
- {
- return myName;
- }
-
- int ShipObject::score() const
- {
- return info.won - info.lost;
- }
-
- int ShipObject::won() const
- {
- return info.won;
- }
-
- int ShipObject::lost() const
- {
- return info.lost;
- }
-
- Team ShipObject::flag() const
- {
- return info.flag;
- }
-
- void ShipObject::flag(Team t)
- {
- // if (info.flag == t) return; // already have it
- info.flag = t;
- if (t == NoTeam)
- flagSwitch->whichChild = SO_SWITCH_NONE;
- else
- flagSwitch->whichChild = int(t);
- }
-
- NetId ShipObject::id() const
- {
- // CHANGED 08/11/93:
- // return NetId(info.myId);
- return NetId((InAddr&)info.myId);
- }
-
- SoSeparator* ShipObject::root() const
- {
- return wholeShip;
- }
-
- void ShipObject::reset()
- {
- int i;
-
- shipVisible = -1; // don't know if visible
- if (self(this)) {
- shipVisibility(FALSE); // hide ship
- camera->aspectRatio = ASPECT;
- camera->heightAngle = HEIGHTANGLE;
- camera->nearDistance = HITHERPLANE;
- camera->farDistance = YONPLANE;
- camera->focalDistance = 50.0;
- camera->viewportMapping = SoCamera::ADJUST_CAMERA;
- }
- else {
- shipSwitch->whichChild = SO_SWITCH_ALL; // show ship
- }
-
- // ship is active
- active(ObjectActive);
-
- // no flag yet
- info.flag = RedTeam; // force an update
- flag(NoTeam);
-
- // set ship mass field
- info.info.mass = mass();
-
- // fill 'er up
- fuel = 1.0;
-
- // initialize position
- info.info.position[0] = 0.0;
- info.info.position[1] = 0.0;
- info.info.position[2] = 0.0;
-
- // initialize velocity
- info.info.velocity[0] = 0.0;
- info.info.velocity[1] = 0.0;
- info.info.velocity[2] = 0.0;
-
- // initialize orientation
- shipOrientation->rotation = SbRotation::identity();
-
- // initialize angular velocity and target angular velocity
- angularVelDir = straightAhead;
- angularVelDirTarget = straightAhead;
-
- // initialize tracking direction and target tracking direction
- trackingDirection = straightAhead;
- trackingDirTarget = straightAhead;
-
- // not exploding
- info.info.explodeTime = 0.0;
-
- // engines off
- info.info.engineOutput = 0.0;
- engineOutputTarget = 0.0;
-
- // can grab any flag
- for (i = 0; i < NUMTEAMS; i++)
- cantGrabFlag[i] = FALSE;
-
- // not firing anything
- info.firingLaser = FALSE;
- laserTemp = 0.0;
- laserSwitch->whichChild = SO_SWITCH_NONE;
- missilesLeft = maxMissiles();
- nextSilo = 0;
- for (i = 0; i < MAXMISSILES; i++) {
- missileTarget[i] = NULL;
- info.missile[i].active = ObjectInactive;
- missileVisible[i] = -1;
- missileVisibility(i, FALSE);
- missileTime[i] = 0.0;
- }
-
- // initialize shields
- for (i = int(FrontShield); i <= int(BottomShield); i++) {
- info.shieldStrength[i] = 1.0;
- info.shieldHits[i][0] = 0.0;
- }
-
- // initialize shield hits
- for (i = 0; i < MAXHITS; i++) {
- hitSwitch[i]->whichChild = SO_SWITCH_NONE;
- float *t = hitMaterial[i]->transparency.startEditing();
- for (int j = 0; j < HITPOINTS; j++) t[j] = 1.0;
- hitMaterial[i]->transparency.finishEditing();
- }
-
- // fill out rest of information from what we've got now
- advance(0.0);
-
- // if local ship, notify server that ship is alive
- SwAliveMessage am;
- server()->send(am);
- }
-
- void ShipObject::advance(float dt)
- {
- if (active() != ObjectActive) return;
- if (!exploding()) {
- advanceEngine(dt);
- advanceLaser(dt);
- }
- advanceShield(dt);
- for (int i = 0; i < MAXMISSILES; i++)
- advanceMissile(i, dt);
- advanceOrientation(dt);
- advancePosition(dt);
- advanceExtra(dt);
- advanceCamera();
- if (exploding()) {
- info.info.explodeTime -= dt / SHIP_EXPLODE;
- if (info.info.explodeTime <= 0.0) {
- info.info.explodeTime = 0.0;
- active(ObjectInactive);
- }
- }
- else {
- // check if I've moved off any flags I can't grab
- for (i = 0; i < NUMTEAMS; i++)
- if (cantGrabFlag[i] && getTeam(Team(i)).state == FlagReady) {
- TeamInfo& t = getTeam(Team(i));
- float dx = t.position[0] - info.info.position[0],
- dy = t.position[1] - info.info.position[1],
- dz = t.position[2] - info.info.position[2];
- if (dx*dx + dy*dy + dz*dz > (sbs+2.0*FLAGSIZE)*(sbs+2.0*FLAGSIZE))
- cantGrabFlag[i] = FALSE;
- }
- }
- }
-
- Active ShipObject::active() const
- {
- return info.info.active;
- }
-
- void ShipObject::active(Active a)
- {
- if (info.info.active == a) return;
- info.info.active = a;
- switch (info.info.active) {
- case ObjectInactive: // not playing
- activeSwitch->whichChild = SO_SWITCH_NONE; // hide all
- break;
- case ObjectActive: // playing
- activeSwitch->whichChild = SO_SWITCH_ALL; // show all
- break;
- case ObjectPaused: // paused
- activeSwitch->whichChild = SO_SWITCH_NONE; // hide all
- break;
- }
- }
-
- int ShipObject::hittable() const
- {
- return (info.info.active == ObjectActive && info.info.explodeTime == 0.0);
- }
-
- float ShipObject::shipBound() const
- {
- return sbs;
- }
-
- int ShipObject::shipVisibility() const
- {
- return shipVisible;
- }
-
- void ShipObject::shipVisibility(int v)
- {
- if (shipVisible == v) return;
- shipVisible = v;
- if (v) {
- shipSwitch->whichChild = SO_SWITCH_ALL;
- }
- else {
- shipSwitch->whichChild = SO_SWITCH_NONE;
- }
- }
-
- ShipInfo& ShipObject::shipInfo()
- {
- return info;
- }
-
- float ShipObject::mass() const
- {
- return DEFAULT_MASS;
- }
-
- int ShipObject::exploding() const
- {
- return (info.info.explodeTime > 0.0);
- }
-
- float ShipObject::explodeTime() const
- {
- return info.info.explodeTime;
- }
-
- SbVec3f ShipObject::position() const
- {
- return SbVec3f(info.info.position);
- }
-
- const SbRotation& ShipObject::orientation() const
- {
- return shipOrientation->rotation.getValue();
- }
-
- const SbVec3f ShipObject::direction() const
- {
- return SbVec3f(info.info.direction);
- }
-
- SbVec3f ShipObject::velocity() const
- {
- return SbVec3f(info.info.velocity);
- }
-
- void ShipObject::position(const SbVec3f& p)
- {
- info.info.position[0] = p[0];
- info.info.position[1] = p[1];
- info.info.position[2] = p[2];
- advance(0.0);
- }
- void ShipObject::orientation(const SbRotation& r)
- {
- shipOrientation->rotation.setValue(r);
- advance(0.0);
- }
- void ShipObject::velocity(const SbVec3f& v)
- {
- info.info.velocity[0] = v[0];
- info.info.velocity[1] = v[1];
- info.info.velocity[2] = v[2];
- advance(0.0);
- }
-
- void ShipObject::targetAngVel(const SbVec3f& avd)
- {
- // limit angular velocity?
- angularVelDirTarget = avd;
- }
-
- float ShipObject::angularAccel() const
- {
- return DEFAULT_ANGACCEL;
- }
-
- void ShipObject::engineOutput(float o)
- {
- if (exploding()) o = 0.0; // no thrust when dead
- else if (fuel == 0.0) {
- if (o > SOLARTHRUST) o = SOLARTHRUST; // no fuel limits max thrust
- else if (o < -SOLARTHRUST) o = -SOLARTHRUST;
- }
- if (o < -1.0) o = -1.0;
- else if (o > 1.0) o = 1.0;
- engineOutputTarget = o;
- }
-
- float ShipObject::engineOutput() const
- {
- return info.info.engineOutput;
- }
-
- float ShipObject::engineForce() const
- {
- return info.info.engineOutput * engineMaxForce();
- }
-
- float ShipObject::engineMaxForce() const
- {
- return DEFAULT_MAXFORCE;
- }
-
- float ShipObject::engineResponse() const
- {
- return DEFAULT_RESPONSE;
- }
-
- float ShipObject::fuelLeft() const
- {
- return fuel;
- }
-
- void ShipObject::fuelLeft(float f)
- {
- if (f < 0.0) f = 0.0;
- else if (f > 1.0) f = 1.0;
- fuel = f;
- }
-
- float ShipObject::fuelRate() const
- {
- return DEFAULT_FUELRATE;
- }
-
- float ShipObject::shield(Shield shieldNum) const
- {
- return info.shieldStrength[int(shieldNum)];
- }
-
- float ShipObject::shieldMaximum() const
- {
- return DEFAULT_MAXSHIELD;
- }
-
- float ShipObject::shieldRecovery() const
- {
- return DEFAULT_SHIELDRECOVERY;
- }
-
- void ShipObject::shieldStrength(float de)
- {
- for (int i = int(FrontShield); i <= int(BottomShield); i++) {
- info.shieldStrength[i] += de;
- if (info.shieldStrength[i] > 1.0)
- info.shieldStrength[i] = 1.0;
- else if (info.shieldStrength[i] < 0.0)
- info.shieldStrength[i] = 0.0;
- }
- }
-
- inline float ShipObject::dissipate(float d) const
- {
- return d;
- }
-
- int ShipObject::fireMissile(ObjectInfo* target)
- {
- if (!missileReady()) return FALSE; // not ready to shoot
-
- // activate a missile and set up its initial stuff
- ObjectInfo& m = info.missile[nextSilo];
- m.active = ObjectActive;
- m.mass = MISSILE_MASS;
- m.explodeTime = 0.0; // not exploding
- m.engineOutput = 1.0; // full power
- SbVec3f p;
- findWorldPosition(missileSource(nextSilo), p);
- m.position[0] = p[0];
- m.position[1] = p[1];
- m.position[2] = p[2];
- m.velocity[0] = info.info.velocity[0];
- m.velocity[1] = info.info.velocity[1];
- m.velocity[2] = info.info.velocity[2];
- m.orientation[0] = info.info.orientation[0];
- m.orientation[1] = info.info.orientation[1];
- m.orientation[2] = info.info.orientation[2];
- m.orientation[3] = info.info.orientation[3];
- m.direction[0] = info.info.direction[0];
- m.direction[1] = info.info.direction[1];
- m.direction[2] = info.info.direction[2];
- missileSwitch[nextSilo]->whichChild = SO_SWITCH_ALL;
- missilePosition[nextSilo]->translation.setValue(m.position);
- missileOrientation[nextSilo]->rotation.setValue(m.orientation);
- missileTime[nextSilo] = MISSILE_TIME; // time to live
- missileTarget[nextSilo] = target; // set target
- missileVisible[nextSilo] = -1;
- missileVisibility(nextSilo, FALSE);
-
- missilesLeft--; // fired!
- nextSilo = (nextSilo + 1) % missileSilos(); // prepare next silo in cycle
- missileChanged();
-
- soundPlay(MissileSound);
-
- return TRUE;
- }
-
- int ShipObject::maxMissiles() const
- {
- return DEFAULT_MAXMISSILES;
- }
-
- int ShipObject::numMissiles() const
- {
- return missilesLeft;
- }
-
- void ShipObject::numMissiles(int m)
- {
- if (m > maxMissiles()) m = maxMissiles();
- missilesLeft = m;
- }
-
- ObjectInfo& ShipObject::missileInfo(int missileNum)
- {
- return info.missile[missileNum];
- }
-
- float ShipObject::missileEnergy() const
- {
- return DEFAULT_MISSILE_ENERGY;
- }
-
- float ShipObject::missileRadius() const
- {
- return mbs * 5.0;
- }
-
- float ShipObject::missileBound() const
- {
- return mbs;
- }
-
- int ShipObject::missileHittable(int num) const
- {
- return (info.missile[num].active == ObjectActive &&
- info.missile[num].explodeTime == 0.0);
- }
-
- int ShipObject::missileVisibility(int num) const
- {
- return missileVisible[num];
- }
-
- void ShipObject::missileVisibility(int num, int v)
- {
- if (info.missile[num].active != ObjectActive && v) v = FALSE;
- if (missileVisible[num] == v) return;
- missileVisible[num] = v;
- if (v) {
- missileSwitch[num]->whichChild = SO_SWITCH_ALL;
- }
- else {
- missileSwitch[num]->whichChild = SO_SWITCH_NONE;
- }
- }
-
- int ShipObject::missileReady() const
- {
- if (active() != ObjectActive || exploding()) // I'm dead
- return FALSE;
- if (missilesLeft <= 0) return FALSE; // no missiles left
- if (missileTime[nextSilo] != 0.0) // reloading, can't fire
- return FALSE;
- return TRUE; // okay
- }
-
- int ShipObject::fireLaser()
- {
- if (active() != ObjectActive || exploding()) // I'm dead
- return FALSE;
-
- if (laserTemp >= 1.0 || info.firingLaser) // too hot or already shooting
- return FALSE;
-
- laserTime = 0.25; // fire for a quarter second
- info.firingLaser = TRUE;
- laserSwitch->whichChild = SO_SWITCH_ALL;
-
- soundPlay(LaserSound);
-
- return TRUE;
- }
-
- float ShipObject::laserHeat() const
- {
- return laserTemp;
- }
-
- float ShipObject::laserPower() const
- {
- return DEFAULT_LASER_POWER;
- }
-
- const SbVec3f& ShipObject::turretPosition() const
- {
- return laserLocation->translation.getValue();
- }
-
- const SbVec3f& ShipObject::trackDirection() const
- {
- return trackingDirection;
- }
-
- void ShipObject::trackDirection(const SbVec3f& t)
- {
- // limit target direction?
- trackingDirTarget = t;
- }
-
- SbVec3f ShipObject::laserPosition() const
- {
- SbVec3f p;
- findWorldPosition(laserLocation->translation.getValue(), p);
- return p;
- }
-
- SbVec3f ShipObject::laserDirection() const
- {
- SbVec3f ld;
- findWorldDirection(trackingDirection, ld);
- return ld;
- }
-
- void ShipObject::advanceExtra(float)
- {
- // do nothing
- }
-
- void ShipObject::read(const ShipInfo& si)
- {
- // given a ship info from a broadcast packet, build other ship data
- // first check to see if anything is newly exploding
- if (info.info.active == ObjectActive && info.info.explodeTime == 0.0 &&
- si.info.explodeTime != 0.0)
- soundPlay(ExplosionSound, explodeVolume((float *)si.info.position, 2.0));
- for (int i = 0; i < MAXMISSILES; i++) {
- if (info.missile[i].active != ObjectActive) continue;
- if (info.missile[i].explodeTime == 0.0 && si.missile[i].explodeTime != 0.0)
- soundPlay(ExplosionSound, explodeVolume((float *)si.missile[i].position));
- }
-
- // set my ship info
- info = si;
-
- // set ship active state
- activeSwitch->whichChild = (info.info.active == ObjectActive) ?
- SO_SWITCH_ALL : SO_SWITCH_NONE;
-
- // set ship visibility
- shipVisibility(info.info.active == ObjectActive &&
- info.info.explodeTime == 0.0);
-
- // set flag visibility
- flag(info.flag);
-
- // set ship position
- shipPosition->translation.setValue(info.info.position);
-
- // set ship rotation
- rotation.setValue(info.info.orientation);
- rotation.getValue(rotateMatrix);
- shipOrientation->rotation.setValue(rotation);
-
- // set laser beam direction, length, and on/off switch
- laserSwitch->whichChild = info.firingLaser ? SO_SWITCH_ALL : SO_SWITCH_NONE;
- laserOrientation->rotation.setValue(SbRotation(straightAhead,
- SbVec3f(info.laserDirection)));
- if (info.firingLaser) {
- for (i = 1; i <= 9; i += 2)
- laserBeamPoints[i][2] = -info.beamLength;
- beamCoord->point.setValues(0, 10, laserBeamPoints);
- }
-
- // set missile positions and orientations
- for (i = 0; i < MAXMISSILES; i++) {
- missileVisibility(i, info.missile[i].explodeTime == 0.0);
- missilePosition[i]->translation.setValue(info.missile[i].position);
- missileOrientation[i]->rotation.setValue(info.missile[i].orientation);
- }
-
- // set shield hit stuff
- for (i = 0; i < MAXHITS; i++) {
- if (info.shieldHits[i][0] == 0.0)
- hitSwitch[i]->whichChild = SO_SWITCH_NONE; // turn off hit
- else {
- hitSwitch[i]->whichChild = SO_SWITCH_ALL; // turn on hit
-
- // set position and scale
- SbVec3f p(info.shieldHits[i] + 1);
- hitTransform[i]->scaleOrientation.setValue(SbRotation(straightAhead, p));
- hitTransform[i]->rotation.setValue(SbRotation(straightAhead, p));
-
- // set hit shading
- float* t = hitMaterial[i]->transparency.startEditing();
- for (int c = 0, m = -HITRES; m <= HITRES; m++)
- for (int n = -HITRES; n <= HITRES; c++, n++) {
- float d = (float)(m*m + n*n) / (2.0*HITRES*HITRES);
- t[c] = 1.0 - (info.shieldHits[i][0] - dissipate(d));
- if (t[c] > 1.0) t[c] = 1.0;
- else if (t[c] < 0.0) t[c] = 0.0;
- }
- hitMaterial[i]->transparency.finishEditing();
- }
- }
- }
-
- float ShipObject::laserHitShip(const Ray& ray) const
- {
- SbVec3f d, o(info.info.position[0] + sx - ray.o[0],
- info.info.position[1] + sx - ray.o[1],
- info.info.position[2] + sx - ray.o[2]);
- rotateMatrix.multMatrixVec(ray.d, d);
- rotateMatrix.multMatrixVec(o, o);
- float x = o[0] / (SHIELDSIZE * sdx),
- y = o[1] / (SHIELDSIZE * sdy),
- z = o[2] / (SHIELDSIZE * sdz);
- float dx = d[0] / (SHIELDSIZE * sdx),
- dy = d[1] / (SHIELDSIZE * sdy),
- dz = d[2] / (SHIELDSIZE * sdz);
- float dist, dd = 1.0 / sqrt(dx*dx + dy*dy + dz*dz);
-
- float l2 = x * x + y * y + z * z; // dist from sphere center
- float tca = (x * dx + y * dy + z * dz) * dd; // closest approach parameter
- float thc;
- if (l2 <= 1.0) { // ray origin inside shields
- dist = tca + sqrt(1.0 - l2 + tca*tca); // good intersection
- }
- else { // starts outside
- if (tca < 0.0) return -1.0; // center behind ray -- miss
- thc = 1.0 - l2 + tca*tca;
- if (thc < 0.0) return -1.0; // to side of sphere -- miss
- thc = sqrt(thc);
- dist = tca - thc; // good intersection
- }
- return dist * dd;
- }
-
- float ShipObject::laserHitMissile(const Ray& r, int n) const
- {
- if (!missileHittable(n)) return -1.0; // no such missile
-
- // intersect against ellipsoid around missile
- SbRotation rot(info.missile[n].orientation); // get rotation
- SbMatrix m;
- rot.getValue(m);
- SbVec3f d, o(info.missile[n].position[0] - r.o[0],
- info.missile[n].position[1] - r.o[1],
- info.missile[n].position[2] - r.o[2]);
- m.multMatrixVec(r.d, d);
- m.multMatrixVec(o, o);
- float x = o[0] / (MISSILE_SIZE * mdx),
- y = o[1] / (MISSILE_SIZE * mdy),
- z = o[2] / (MISSILE_SIZE * mdz);
- float dx = d[0] / (MISSILE_SIZE * mdx),
- dy = d[1] / (MISSILE_SIZE * mdy),
- dz = d[2] / (MISSILE_SIZE * mdz);
- float dist, dd = 1.0 / sqrt(dx*dx + dy*dy + dz*dz);
-
- float l2 = x * x + y * y + z * z; // dist from sphere center
- float tca = (x * dx + y * dy + z * dz) * dd; // closest approach parameter
- float thc;
- if (l2 <= 1.0) { // ray origin inside shields
- dist = tca + sqrt(1.0 - l2 + tca*tca); // good intersection
- }
- else { // starts outside
- if (tca < 0.0) return -1.0; // center behind ray -- miss
- thc = 1.0 - l2 + tca*tca;
- if (thc < 0.0) return -1.0; // to side of sphere -- miss
- thc = sqrt(thc);
- dist = tca - thc; // good intersection
- }
- return dist * dd;
- }
-
- float ShipObject::laserHitAsteroid(const Ray& r, int a) const
- {
- float* ap = asteroidPosition(a);
- float radius = asteroidRadius(a);
- float x = ap[0] - r.o[0],
- y = ap[1] - r.o[1],
- z = ap[2] - r.o[2];
- float dx = r.d[0],
- dy = r.d[1],
- dz = r.d[2];
- float d, dd = sqrt(dx * dx + dy * dy + dz * dz);
- float r2 = radius * radius;
-
- float l2 = x * x + y * y + z * z; // dist from sphere center
- float tca = (x * dx + y * dy + z * dz) / dd; // closest approach parameter
- if (l2 <= r2) { // ray origin inside shields
- d = tca + sqrt(r2 - l2 + tca*tca); // good intersection
- }
- else { // starts outside
- if (tca < 0.0) return -1.0; // center behind ray -- miss
- float thc = r2 - l2 + tca*tca;
- if (thc < 0.0) return -1.0; // to side of sphere -- miss
- d = tca - sqrt(thc); // good intersection
- }
- return d / dd;
- }
-
- float ShipObject::missileHitShip(const Ray& r, float radius)
- const
- {
- // find ray in ship space
- float x = info.info.position[0] + sx - r.o[0],
- y = info.info.position[1] + sy - r.o[1],
- z = info.info.position[2] + sz - r.o[2];
- float dx = r.d[0] - info.info.velocity[0],
- dy = r.d[1] - info.info.velocity[1],
- dz = r.d[2] - info.info.velocity[2];
- float d, dd = sqrt(dx * dx + dy * dy + dz * dz);
- if (dd < 1e-4) return -1.0; // ray not moving
- radius += shipBound(); // increase radius by ship size
- float r2 = radius * radius;
-
- float l2 = x * x + y * y + z * z; // dist from sphere center
- float tca = (x * dx + y * dy + z * dz) / dd; // closest approach parameter
- if (l2 <= r2) { // ray origin inside shields
- d = tca + sqrt(r2 - l2 + tca*tca); // good intersection
- }
- else { // starts outside
- if (tca < 0.0) return -1.0; // center behind ray -- miss
- float thc = r2 - l2 + tca*tca;
- if (thc < 0.0) return -1.0; // to side of sphere -- miss
- d = tca - sqrt(thc); // good intersection
- }
- return d / dd;
- }
-
- float ShipObject::missileHitMissile(const Ray& r,
- float radius, int n) const
- {
- if (!missileHittable(n)) return -1.0; // no such missile
-
- float x = info.missile[n].position[0] - r.o[0],
- y = info.missile[n].position[1] - r.o[1],
- z = info.missile[n].position[2] - r.o[2];
- float dx = r.d[0] - info.missile[n].velocity[0],
- dy = r.d[1] - info.missile[n].velocity[1],
- dz = r.d[2] - info.missile[n].velocity[2];
- float d, dd = sqrt(dx * dx + dy * dy + dz * dz);
- if (dd < 1e-4) return -1.0; // ray not moving
- float r2 = radius * radius;
-
- float l2 = x * x + y * y + z * z; // dist from sphere center
- float tca = (x * dx + y * dy + z * dz) / dd; // closest approach parameter
- if (l2 <= r2) { // ray origin inside shields
- d = tca + sqrt(r2 - l2 + tca*tca); // good intersection
- }
- else { // starts outside
- if (tca < 0.0) return -1.0; // center behind ray -- miss
- float thc = r2 - l2 + tca*tca;
- if (thc < 0.0) return -1.0; // to side of sphere -- miss
- d = tca - sqrt(thc); // good intersection
- }
- return d / dd;
- }
-
- float ShipObject::missileHitAsteroid(const Ray& r,
- float radius, int a) const
- {
- float* ap = asteroidPosition(a);
- radius += asteroidRadius(a);
- float x = ap[0] - r.o[0],
- y = ap[1] - r.o[1],
- z = ap[2] - r.o[2];
- float dx = r.d[0],
- dy = r.d[1],
- dz = r.d[2];
- float d, dd = sqrt(dx * dx + dy * dy + dz * dz);
- if (dd < 1e-4) return -1.0; // ray not moving
- float r2 = radius * radius;
-
- float l2 = x * x + y * y + z * z; // dist from sphere center
- float tca = (x * dx + y * dy + z * dz) / dd; // closest approach parameter
- if (l2 <= r2) { // ray origin inside shields
- d = tca + sqrt(r2 - l2 + tca*tca); // good intersection
- }
- else { // starts outside
- if (tca < 0.0) return -1.0; // center behind ray -- miss
- float thc = r2 - l2 + tca*tca;
- if (thc < 0.0) return -1.0; // to side of sphere -- miss
- d = tca - sqrt(thc); // good intersection
- }
- return d / dd;
- }
-
- void ShipObject::explodeShip(InAddr killer)
- {
- // Note: only called for local object
- // I'm blowing up now
- info.info.explodeTime = 1.0;
-
- // drop whatever flag I had
- dropFlag();
-
- // send message that I was killed
- if (!NetId(killer).isAny()) {
- SwKilledMessage m;
- m.killer = killer; // who destroyed me
- m.victim = id(); // who got destroyed (me!)
- server()->send(m);
- }
-
- // can't target when I'm dead
- noTarget();
-
- // no thrust when blowing up
- info.info.engineOutput = engineOutputTarget = 0.0;
- fuel = 0.0;
- thrustChanged();
- fuelChanged();
-
- // shut off laser
- info.firingLaser = FALSE;
- laserSwitch->whichChild = SO_SWITCH_NONE;
- laserTemp = 0.0;
- laserTime = 0.0;
- laserChanged();
-
- // take away remaining missiles
- missilesLeft = 0;
- missileChanged();
-
- // shields all gone (and not hits visible)
- for (int i = int(FrontShield); i <= int(BottomShield); i++)
- info.shieldStrength[i] = 0.0;
- shieldsChanged();
- for (i = 0; i < MAXHITS; i++) {
- info.shieldHits[i][0] = 0.0;
- hitSwitch[i]->whichChild = SO_SWITCH_NONE;
- }
-
- soundPlay(ExplosionSound);
- }
-
- void ShipObject::explodeMissile(const SwHitMessage& m)
- {
- if (!self(m.victim)) { // remote ship
- server()->send(m); // send message
- return;
- }
-
- if (!missileHittable(m.missileNum)) return; // not active or exploding
-
- // now exploding
- info.missile[m.missileNum].explodeTime = 1.0;
- if (missileTime[m.missileNum] < MISSILE_EXPLODE)
- missileTime[m.missileNum] = MISSILE_EXPLODE;
-
- // missile geometry invisible (explosion is)
- missileSwitch[m.missileNum]->whichChild = SO_SWITCH_NONE;
-
- // modify position and velocity due to impact
- info.missile[m.missileNum].position[0] = m.position[0];
- info.missile[m.missileNum].position[1] = m.position[1];
- info.missile[m.missileNum].position[2] = m.position[2];
- info.missile[m.missileNum].velocity[0] += m.velocityChange[0];
- info.missile[m.missileNum].velocity[1] += m.velocityChange[1];
- info.missile[m.missileNum].velocity[2] += m.velocityChange[2];
-
- soundPlay(ExplosionSound, explodeVolume(info.missile[m.missileNum].position));
- }
-
- void ShipObject::dropFlag()
- {
- if (flag() == NoTeam) return;
-
- SwDropFlagMessage m;
- m.team = flag();
- SbVec3f p;
- rotation.getValue(rotateMatrix);
- findWorldPosition(flagPosition, p);
- m.position[0] = p[0];
- m.position[1] = p[1];
- m.position[2] = p[2];
- server()->send(m);
-
- if (self(this)) // can't grab flag til I move
- cantGrabFlag[int(flag())] = TRUE;
-
- flag(NoTeam);
- flagChanged();
- }
-
- void ShipObject::grabFlag(Team t)
- {
- if (flag() != NoTeam || cantGrabFlag[int(t)]) return;
-
- SwGrabFlagMessage m;
- m.team = t;
- server()->send(m);
- }
-
- void ShipObject::hitShield(const SwHitMessage& hm)
- {
- if (!self(hm.victim)) { // remote ship
- server()->send(hm); // send message
- return;
- }
-
- if (!hittable()) return; // not active or exploding
-
- // modify ship's velocity due to impact
- // what about angular velocity?
- info.info.velocity[0] += hm.velocityChange[0];
- info.info.velocity[1] += hm.velocityChange[1];
- info.info.velocity[2] += hm.velocityChange[2];
-
- // find available hit slot
- for (int b = 0, j = 1; j < MAXHITS; j++)
- if (info.shieldHits[j][0] < info.shieldHits[b][0])
- b = j;
-
- // turn on hit geometry
- SbVec3f i(hm.position); // local direction of hit
- hitSwitch[b]->whichChild = SO_SWITCH_ALL;
- hitTransform[b]->scaleOrientation.setValue(SbRotation(straightAhead, i));
- hitTransform[b]->rotation.setValue(SbRotation(straightAhead, i));
- info.shieldHits[b][0] = 10.0 * hm.energy / DEFAULT_LASER_POWER;
- if (info.shieldHits[b][0] > 1.25) info.shieldHits[b][0] = 1.25;
- info.shieldHits[b][1] = i[0];
- info.shieldHits[b][2] = i[1];
- info.shieldHits[b][3] = i[2];
-
- // set hit shading
- float* t = hitMaterial[b]->transparency.startEditing();
- for (int c = 0, m = -HITRES; m <= HITRES; m++)
- for (int n = -HITRES; n <= HITRES; c++, n++) {
- float d = (float)(m*m + n*n) / (2.0*HITRES*HITRES);
- t[c] = 1.0 - (info.shieldHits[b][0] - dissipate(d));
- if (t[c] > 1.0) t[c] = 1.0;
- else if (t[c] < 0.0) t[c] = 0.0;
- }
- hitMaterial[b]->transparency.finishEditing();
-
- // remove strength from shield
- int s = int(shieldNumber(i)); // which shield was hit
- if (info.shieldStrength[s] > 0.0) { // shield still up
- if ((info.shieldStrength[s] -= hm.energy / shieldMaximum()) < 0.0)
- if (info.shieldStrength[s] > -0.1)
- info.shieldStrength[s] = 0.0; // shield has collapsed
- else
- explodeShip(hm.hitter); // too much extra, blow up
- }
- else // shield was collapsed
- explodeShip(hm.hitter); // blow up ship
- }
-
- ShipObject::Shield ShipObject::shieldNumber(const SbVec3f& i) const
- {
- // find which shield
- // Note: i must be on the unit sphere
- if (i[1] > M_SQRT1_2) return TopShield;
- if (i[1] < -M_SQRT1_2) return BottomShield;
- if (fabs(i[0]) > fabs(i[2])) {
- if (i[0] < 0.0) return LeftShield;
- else return RightShield;
- }
- else {
- if (i[2] < 0.0) return FrontShield;
- else return RearShield;
- }
- }
-
- void ShipObject::advanceCamera()
- {
- if (self(this)) {
- SbVec3f cp;
- findWorldDirection(cockpit, cp);
- ::position->translation.setValue(-info.info.position[0] - cp[0],
- -info.info.position[1] - cp[1],
- -info.info.position[2] - cp[2]);
- camera->orientation.setValue(orientation());
- orientation().getValue(rotateMatrix);
- }
- }
-
- void ShipObject::advancePosition(float dt)
- {
- // advance ship velocity in world space
- float dv = dt * engineForce() / mass(); // change in velocity
- info.info.velocity[0] += dv * info.info.direction[0];
- info.info.velocity[1] += dv * info.info.direction[1];
- info.info.velocity[2] += dv * info.info.direction[2];
-
- // advance ship position in world space
- info.info.position[0] += dt * info.info.velocity[0];
- info.info.position[1] += dt * info.info.velocity[1];
- info.info.position[2] += dt * info.info.velocity[2];
- shipPosition->translation.setValue(info.info.position);
- }
-
- void ShipObject::advanceOrientation(float dt)
- {
- // advance angular velocity (toward target angular velocity)
- advanceTurn(angularVelDirTarget, angularVelDir, angularAccel(), dt);
-
- // advance ship orientation
- SbVec3f a = straightAhead.cross(angularVelDir);
- float ang = asin(a.length());
- SbRotation angVel(a, ang * ANGVELMULT * dt);
- shipOrientation->rotation.setValue(angVel * orientation());
- orientation().getValue(info.info.orientation[0], info.info.orientation[1],
- info.info.orientation[2], info.info.orientation[3]);
-
- // compute world space forward vector
- SbVec3f f;
- findWorldDirection(straightAhead, f);
- f.getValue(info.info.direction[0], info.info.direction[1],
- info.info.direction[2]);
- }
-
- void ShipObject::advanceEngine(float dt)
- {
- // use up fuel if thrusting
- if (fuel > 0.0 && fabs(engineOutput()) > 0.0) {
- fuel -= dt * fuelRate() * fabs(engineOutput());
- if (fuel <= 0.0) fuel = 0.0; // ran out of fuel
- fuelChanged();
- }
-
- // limit thrust if out of fuel
- if (fuel == 0.0) {
- if (engineOutputTarget > SOLARTHRUST)
- engineOutputTarget = SOLARTHRUST;
- else if (engineOutputTarget < -SOLARTHRUST)
- engineOutputTarget = -SOLARTHRUST;
- if (info.info.engineOutput > SOLARTHRUST)
- info.info.engineOutput = SOLARTHRUST;
- else if (info.info.engineOutput < -SOLARTHRUST)
- info.info.engineOutput = -SOLARTHRUST;
- }
-
- // get thrust level toward target
- if (engineOutputTarget != info.info.engineOutput) {
- if (fabs(engineOutputTarget - info.info.engineOutput) <= engineResponse())
- info.info.engineOutput = engineOutputTarget;
- else if (engineOutputTarget > info.info.engineOutput)
- info.info.engineOutput += engineResponse();
- else
- info.info.engineOutput -= engineResponse();
- thrustChanged();
- newEngineOutput();
- }
- }
-
- void ShipObject::advanceShield(float dt)
- {
- // recover shield strength
- int shieldChange = FALSE;
- float sr = dt * shieldRecovery() / shieldMaximum();
- if (info.info.explodeTime != 0.0) sr = 0.0; // no recovery if dead
- for (int i = int(FrontShield); i <= int(BottomShield); i++)
- if (info.shieldStrength[i] < 1.0 && // if not full strength
- info.shieldStrength[i] != 0.0) { // and not collapsed
- if (info.shieldStrength[i] + sr >= 1.0)
- info.shieldStrength[i] = 1.0; // fully recovered
- else
- info.shieldStrength[i] += sr; // recover some more
- shieldChange = TRUE;
- }
- if (shieldChange) shieldsChanged();
-
- // dissipate hits
- float de = dt * DISSIPATE_RATE;
- for (i = 0; i < MAXHITS; i++) {
- if (info.shieldHits[i][0] > 0.0) {
- info.shieldHits[i][0] -= de; // dissipate impact
- if (info.shieldHits[i][0] <= 0.0) { // dissipated -- shut off hit
- info.shieldHits[i][0] = 0.0;
- hitSwitch[i]->whichChild = SO_SWITCH_NONE;
- }
- else { // still dissipating
- float* t = hitMaterial[i]->transparency.startEditing();
- for (int c = 0, j = -HITRES; j <= HITRES; j++)
- for (int k = -HITRES; k <= HITRES; c++, k++) {
- float d = (float)(j*j + k*k) / (2.0*HITRES*HITRES);
- t[c] = 1.0 - (info.shieldHits[i][0] - dissipate(d));
- if (t[c] > 1.0) t[c] = 1.0;
- else if (t[c] < 0.0) t[c] = 0.0;
- }
- hitMaterial[i]->transparency.finishEditing();
- }
- }
- }
- }
-
- void ShipObject::advanceMissile(int num, float dt)
- {
- if (info.missile[num].active==ObjectActive) { // missile exists
- ObjectInfo& m = info.missile[num];
- float t = (missileTime[num] > dt) ? dt : missileTime[num];
-
- // if exploding, advance time and return
- if (m.explodeTime > 0.0) {
- m.explodeTime -= dt / MISSILE_EXPLODE;
- if (m.explodeTime <= 0.0) { // done exploding
- m.active = ObjectInactive;
- missileVisibility(num, FALSE);
- m.explodeTime = 0.0;
- }
- }
- else {
- if (missileTarget[num] && (missileTarget[num]->active != ObjectActive ||
- missileTarget[num]->explodeTime != 0.0))
- missileTarget[num] = NULL; // target disappeared
- if (missileTarget[num]) { // target exists; aim at it
- if (missileTime[num] < MISSILE_TIME - GLIDE_TIME) {
- // make rough prediction of target's relative position at impact time
- SbVec3f p(missileTarget[num]->position);
- p[0] -= m.position[0];
- p[1] -= m.position[1];
- p[2] -= m.position[2]; // rel. pos. w/o velocity
- SbVec3f v(m.velocity);
- float vm = v.length(); // speed
- if (vm > 1.0) { // only if missile moving
- float tti = p.length() / vm; // approximate time to impact
- p[0] = missileTarget[num]->position[0] - m.position[0] +
- tti * (missileTarget[num]->velocity[0] - 0.75*m.velocity[0]);
- p[1] = missileTarget[num]->position[1] - m.position[1] +
- tti * (missileTarget[num]->velocity[1] - 0.75*m.velocity[1]);
- p[2] = missileTarget[num]->position[2] - m.position[2] +
- tti * (missileTarget[num]->velocity[2] - 0.75*m.velocity[2]);
- }
-
- // turn toward predicted position
- p.normalize(); // direction to target
- SbVec3f d(m.direction); // direction missile pointing
- advanceTurn(p, d, MISSILE_ANG, dt); // turn toward target
- SbRotation r(straightAhead, d); // find rotation
- r.getValue(m.orientation[0], m.orientation[1],
- m.orientation[2], m.orientation[3]);
- missileOrientation[num]->rotation.setValue(r);
- d.getValue(m.direction[0], m.direction[1], m.direction[2]);
- }
- }
- else { // no target
- // got me. I guess we go in a straight line.
- }
-
- // advance missile velocity in world space
- float dv = t * m.engineOutput * MISSILE_ACCEL;
- m.velocity[0] += dv * m.direction[0];
- m.velocity[1] += dv * m.direction[1];
- m.velocity[2] += dv * m.direction[2];
-
- {
- Ray r;
- r.o.setValue(m.position);
- r.d.setValue(m.velocity);
-
- float hitTime, bestTime = t;
- int bestShip = -1, bestMissile = -1, bestAsteroid = -1;
- for (int i = 0; i < MAXPLAYERS; i++) {
- ShipObject* s = getPlayer(i);
- if (!s || s->active() != ObjectActive ||
- (self(s) && missileTime[num] >= MISSILE_TIME - GLIDE_TIME))
- continue;
- ShipInfo& si = s->shipInfo();
- hitTime = s->missileHitShip(r, missileRadius());
- if (hitTime >= 0.0 && hitTime <= bestTime) {
- bestTime = hitTime;
- bestShip = i;
- bestMissile = -1;
- }
-
- if (!self(s)) {
- for (int j = 0; j < MAXMISSILES; j++) {
- hitTime = s->missileHitMissile(r, missileRadius(), j);
- if (hitTime >= 0.0 && hitTime <= bestTime) {
- bestTime = hitTime;
- bestShip = i;
- bestMissile = j;
- }
- }
- }
- }
- for (i = 0; i < numberAsteroids(); i++) {
- hitTime = missileHitAsteroid(r, missileRadius(), i);
- if (hitTime >= 0.0 && hitTime <= bestTime) {
- bestTime = hitTime;
- bestAsteroid = i;
- }
- }
-
- if (bestAsteroid != -1) {
- SwHitMessage hm;
- hm.hitter = id();
- hm.victim = id();
- hm.missileNum = num;
- hm.energy = missileEnergy();
- hm.position[0] = r.o[0] + bestTime * r.d[0];
- hm.position[1] = r.o[1] + bestTime * r.d[1];
- hm.position[2] = r.o[2] + bestTime * r.d[2];
- hm.velocityChange[0] = -m.velocity[0];
- hm.velocityChange[1] = -m.velocity[1];
- hm.velocityChange[2] = -m.velocity[2];
- explodeMissile(hm); // explode my missile
- }
- else if (bestShip != -1) {
- SwHitMessage hm;
- hm.hitter = id();
- hm.victim = id();
- hm.missileNum = num;
- hm.energy = missileEnergy();
- hm.position[0] = r.o[0] + bestTime * r.d[0];
- hm.position[1] = r.o[1] + bestTime * r.d[1];
- hm.position[2] = r.o[2] + bestTime * r.d[2];
-
- ObjectInfo* hitObject;
- ShipObject* hitShip = getPlayer(bestShip);
- ShipInfo& hitInfo = hitShip->shipInfo();
- if (bestMissile == -1) {
- hitObject = &(hitInfo.info);
-
- // FIXME -- compute correct momentum transfer
- // make missile explosion travel with hit ship
- hm.velocityChange[0] = hitObject->velocity[0]-m.velocity[0];
- hm.velocityChange[1] = hitObject->velocity[1]-m.velocity[1];
- hm.velocityChange[2] = hitObject->velocity[2]-m.velocity[2];
- explodeMissile(hm); // explode my missile
-
- // setup message for hit ship
- hm.victim = hitShip->id();
- hm.missileNum = -1;
- hm.velocityChange[0] = 0.0;
- hm.velocityChange[1] = 0.0;
- hm.velocityChange[2] = 0.0;
-
- // find local direction of impact at time bestTime
- SbVec3f hitPos(hm.position[0], hm.position[1], hm.position[2]);
- // ship would have moved when impact occurred
- hitPos -= SbVec3f(bestTime * hitObject->velocity[0],
- bestTime * hitObject->velocity[1],
- bestTime * hitObject->velocity[2]);
- hitShip->findLocalPosition(hitPos, hitPos);
- hm.position[0] = hitPos[0] / (SHIELDSIZE * sdx);
- hm.position[1] = hitPos[1] / (SHIELDSIZE * sdy);
- hm.position[2] = hitPos[2] / (SHIELDSIZE * sdz);
-
- // send hit message to hit ship
- hitShip->hitShield(hm);
- }
- else {
- hitObject = &(hitInfo.missile[bestMissile]);
-
- // FIXME -- compute correct momentum transfer
- // make missile explosion travel with hit ship
- hm.velocityChange[0] = hitObject->velocity[0]-m.velocity[0];
- hm.velocityChange[1] = hitObject->velocity[1]-m.velocity[1];
- hm.velocityChange[2] = hitObject->velocity[2]-m.velocity[2];
- explodeMissile(hm); // explode my missile
-
- // setup message for hit missile's ship
- hm.victim = hitShip->id();
- hm.missileNum = bestMissile;
- hm.velocityChange[0] = m.velocity[0]-hitObject->velocity[0];
- hm.velocityChange[1] = m.velocity[1]-hitObject->velocity[1];
- hm.velocityChange[2] = m.velocity[2]-hitObject->velocity[2];
-
- // send hit message to hit missile's ship
- hitShip->explodeMissile(hm);
- }
- }
- }
-
- if (missileTime[num] - dt <= MISSILE_EXPLODE && m.explodeTime == 0.0) {
- SwHitMessage hm;
- hm.hitter = id();
- hm.victim = id();
- hm.missileNum = num;
- hm.energy = missileEnergy();
- hm.position[0] = m.position[0];
- hm.position[1] = m.position[1];
- hm.position[2] = m.position[2];
- hm.velocityChange[0] = 0.0;
- hm.velocityChange[1] = 0.0;
- hm.velocityChange[2] = 0.0;
- explodeMissile(hm);
- }
- }
-
- // advance missile position in world space
- m.position[0] += t * m.velocity[0];
- m.position[1] += t * m.velocity[1];
- m.position[2] += t * m.velocity[2];
- missilePosition[num]->translation.setValue(m.position);
- }
-
- // decrement reload time if reloading
- if (missileTime[num] > 0.0)
- if ((missileTime[num] -= dt) <= 0.0) {
- missileTime[num] = 0.0; // finished reloading
- missileChanged();
- }
- }
-
- void ShipObject::advanceLaser(float dt)
- {
- int laserTempChanged = (laserTemp > 0.0 || info.firingLaser);
-
- // rotate laser (toward target direction)
- advanceTurn(trackingDirTarget, trackingDirection, MAXTRACKACC, dt);
- laserOrientation->rotation.setValue(SbRotation(straightAhead,
- trackingDirection));
- SbVec3f ld = laserDirection();
- findWorldDirection(trackingDirection, ld);
- info.laserDirection[0] = ld[0];
- info.laserDirection[1] = ld[1];
- info.laserDirection[2] = ld[2];
-
- // cool off laser
- if (laserTemp > 0.0) {
- laserTemp -= 0.1 * (dt / 1.0);
- if (laserTemp < 0.0) laserTemp = 0.0;
- }
-
- if (info.firingLaser) { // if we're firing check hits
- float t = (laserTime >= dt) ? dt : laserTime;
- Ray r;
- r.o = laserPosition();
- r.d = ld;
-
- float hitDist, bestDist = LASERRANGE;
- int bestShip = -1, bestMissile = -1, bestAsteroid = -1;
-
- for (int i = 0; i < MAXPLAYERS; i++) {
- ShipObject* s = getPlayer(i);
- if (!s || s->active() != ObjectActive) continue;
- ShipInfo& si = s->shipInfo();
- if (!self(s)) {
- hitDist = s->laserHitShip(r);
- if (hitDist >= 0.0 && hitDist < bestDist) {
- bestDist = hitDist;
- bestShip = i;
- bestMissile = -1;
- }
- }
-
- // check against missiles
- for (int j = 0; j < MAXMISSILES; j++) {
- hitDist = s->laserHitMissile(r, j);
- if (hitDist >= 0.0 && hitDist < bestDist) {
- bestDist = hitDist;
- bestShip = i;
- bestMissile = j;
- }
- }
- }
-
- // check against asteroids
- for (i = 0; i < numberAsteroids(); i++) {
- hitDist = laserHitAsteroid(r, i);
- if (hitDist >= 0.0 && hitDist <= bestDist) {
- bestDist = hitDist;
- bestAsteroid = i;
- }
- }
-
- if (bestAsteroid != -1) {
- // do nothing
- }
- else if (bestShip != -1) {
- ShipObject* s = getPlayer(bestShip);
-
- SwHitMessage hm;
- hm.hitter = id();
- hm.victim = s->id();
- hm.missileNum = bestMissile;
- hm.energy = t * laserPower();
- hm.position[0] = r.o[0] + bestDist * r.d[0];
- hm.position[1] = r.o[1] + bestDist * r.d[1];
- hm.position[2] = r.o[2] + bestDist * r.d[2];
- hm.velocityChange[0] = 0.0;
- hm.velocityChange[1] = 0.0;
- hm.velocityChange[2] = 0.0;
-
- if (bestMissile == -1) {
- // find local direction of impact
- SbVec3f hitPos(hm.position[0], hm.position[1], hm.position[2]);
- s->findLocalPosition(hitPos, hitPos);
- hm.position[0] = hitPos[0] / (SHIELDSIZE * sdx);
- hm.position[1] = hitPos[1] / (SHIELDSIZE * sdy);
- hm.position[2] = hitPos[2] / (SHIELDSIZE * sdz);
- s->hitShield(hm);
- }
- else {
- s->explodeMissile(hm);
- }
- }
-
- // set length of beam
- info.beamLength = bestDist;
- for (i = 1; i <= 9; i += 2)
- laserBeamPoints[i][2] = -info.beamLength;
- beamCoord->point.setValues(0, 10, laserBeamPoints);
-
- // heat up laser
- laserTemp += 1.5 * (t / 1.0);
-
- // decrement time left to fire laser
- if ((laserTime -= dt) <= 0.0) { // out of time
- info.firingLaser = FALSE; // not firing
- laserSwitch->whichChild = SO_SWITCH_NONE; // turn beam off
- }
- }
-
- if (laserTempChanged) laserChanged();
- }
-
- void ShipObject::advanceTurn(const SbVec3f& t, SbVec3f& v,
- float maxAcc, float dt) const
- {
- float da = dt * maxAcc; // max angle we can turn
- if (da >= M_PI || t.dot(v) >= cos(da)) { // < max or max to big
- v = t; // reached the target
- }
- else { // can't reach target
- SbRotation turn(v.cross(t), da); // make maximum rotation
- SbMatrix m;
- turn.getValue(m); // make rotation matrix
- m.multDirMatrix(v, v); // rotate towards target
- }
- }
-
- void ShipObject::findWorldDirection(const SbVec3f& in,
- SbVec3f& out) const
- {
- rotateMatrix.multDirMatrix(in, out);
- }
-
- void ShipObject::findWorldPosition(const SbVec3f& in,
- SbVec3f& out) const
- {
- rotateMatrix.multVecMatrix(in, out);
- out += position();
- }
-
- void ShipObject::findLocalDirection(const SbVec3f& in,
- SbVec3f& out) const
- {
- rotateMatrix.multMatrixVec(in, out);
- }
-
- void ShipObject::findLocalPosition(const SbVec3f& in,
- SbVec3f& out) const
- {
- out = in;
- out -= position();
- rotateMatrix.multMatrixVec(out, out);
- }
-
- void ShipObject::findLocalPosition(float p[3],
- SbVec3f& out) const
- {
- out.setValue(p);
- out -= position();
- rotateMatrix.multMatrixVec(out, out);
- }
-
- SoSeparator* ShipObject::makeLaserTurret()
- {
- SoSeparator* turretRoot = new SoSeparator; // beam and turret under here
-
- // build transformation for entire turret
- turretRoot->addChild(laserLocation);
- turretRoot->addChild(laserOrientation);
-
- // build laser turret
- SoSeparator* turretStuff = new SoSeparator; // turret under here
- turretRoot->addChild(turretStuff);
- turretStuff->setGLRenderCaching(TRUE); // turret never changes
-
- SoComplexity* turretCmplx = new SoComplexity; // not too complex turret
- turretStuff->addChild(turretCmplx);
- turretCmplx->type = SoComplexity::OBJECT_SPACE;
- turretCmplx->value = 0.1;
-
- SoMaterial* turretMaterial = new SoMaterial; // turret material
- turretStuff->addChild(turretMaterial);
-
- SoSphere* turret = new SoSphere; // turret is a sphere
- turretStuff->addChild(turret);
- turret->radius = TURRETRADIUS;
-
- SoTranslation* barrelPos = new SoTranslation; // barrel is offset
- turretStuff->addChild(barrelPos);
- barrelPos->translation.setValue(0.0, 0.0, -TURRETRADIUS);
-
- SoRotation* barrelRotation = new SoRotation; // and points along z-axis
- turretStuff->addChild(barrelRotation);
- barrelRotation->rotation.setValue(SbRotation(SbVec3f(0.0, 1.0, 0.0),
- straightAhead));
-
- SoMaterial* barrelMaterial = new SoMaterial; // barrel material
- turretStuff->addChild(barrelMaterial);
- barrelMaterial->diffuseColor.setValue(0.2, 0.2, 0.2);
-
- SoCylinder* barrel = new SoCylinder; // barrel is a cylinder
- turretStuff->addChild(barrel);
- barrel->radius = BEAMRADIUS;
- barrel->height = TURRETRADIUS / 2.0; // half turret radius
-
- return turretRoot;
- }
-
- SoSeparator* ShipObject::makeLaserBeam()
- {
- // build laser beam
- SoSeparator* laserBeam = new SoSeparator; // all beam stuff under here
-
- // set beam transformation
- laserBeam->addChild(laserLocation);
- laserBeam->addChild(laserOrientation);
-
- SoSeparator* beamStuff = new SoSeparator;
- laserBeam->addChild(beamStuff);
- beamStuff->setGLRenderCaching(TRUE); // beam never changes
-
- SoLightModel* beamLight = new SoLightModel; // laser beam is it's own light
- beamStuff->addChild(beamLight);
- beamLight->model = SoLightModel::BASE_COLOR;
-
- SoMaterial* beamMaterial = new SoMaterial; // laser beam is bright red
- beamStuff->addChild(beamMaterial);
- beamMaterial->diffuseColor.setValue(1.0, 0.0, 0.0);
-
- beamStuff->addChild(beamCoord = new SoCoordinate3);
- for (int i = 0; i <= 4; i++) {
- laserBeamPoints[2*i][0] = BEAMRADIUS * cos(M_PI * (float)(i%4) / 2.0);
- laserBeamPoints[2*i][1] = BEAMRADIUS * sin(M_PI * (float)(i%4) / 2.0);
- laserBeamPoints[2*i][2] = -3.0 * TURRETRADIUS / 2.0;
- laserBeamPoints[2*i+1][0] = BEAMRADIUS * cos(M_PI * (float)(i%4) / 2.0);
- laserBeamPoints[2*i+1][1] = BEAMRADIUS * sin(M_PI * (float)(i%4) / 2.0);
- laserBeamPoints[2*i+1][2] = -LASERRANGE;
- }
- beamCoord->point.setValues(0, 10, laserBeamPoints);
-
- SoTriangleStripSet* beam = new SoTriangleStripSet;
- beamStuff->addChild(beam);
- beam->startIndex = 0;
- beam->numVertices = 10;
-
- return laserBeam;
- }
-
- SoSeparator* ShipObject::makeShields()
- {
- SoSeparator* hitsAll = new SoSeparator;
-
- // hits makes their own light
- SoLightModel* hitLight = new SoLightModel;
- hitsAll->addChild(hitLight);
- hitLight->model = SoLightModel::BASE_COLOR;
-
- // hit material changes per vertex
- SoMaterialBinding* hitBinding = new SoMaterialBinding;
- hitsAll->addChild(hitBinding);
- hitBinding->value = SoMaterialBinding::PER_VERTEX;
-
- // hits glow greenish
- SoMaterial* hitColor = new SoMaterial;
- hitsAll->addChild(hitColor);
- hitColor->diffuseColor.setValue(0.25, 1.0, 0.25);
-
- // all hits have the same geometry
- SoCoordinate3* hitPoints = new SoCoordinate3;
- hitsAll->addChild(hitPoints);
- hitPoints->point.insertSpace(0, HITPOINTS);
- SbVec3f* points = hitPoints->point.startEditing();
- for (int c = 0, i = -HITRES; i <= HITRES; i++)
- for (int j = -HITRES; j <= HITRES; c++, j++) {
- int k = (abs(i) > abs(j) ? abs(i) : abs(j));
- points[c][0] = sin(HITSPREAD * (float)i / HITRES) *
- cos(HITSPREAD * (float)j / HITRES);
- points[c][1] = cos(HITSPREAD * (float)i / HITRES) *
- sin(HITSPREAD * (float)j / HITRES);
- points[c][2] = -cos(HITSPREAD * (float)k / HITRES);
- points[c].normalize();
- }
- hitPoints->point.finishEditing();
-
- // make shape (we'll reference it for each hit)
- SoQuadMesh* hitGeometry = new SoQuadMesh;
- hitGeometry->startIndex = 0;
- hitGeometry->verticesPerColumn = HITSIZE;
- hitGeometry->verticesPerRow = HITSIZE;
-
- // make individual hits
- for (i = 0; i < MAXHITS; i++) {
- hitsAll->addChild(hitSwitch[i] = new SoSwitch);
- SoSeparator* hitStuff = new SoSeparator;
- hitSwitch[i]->addChild(hitStuff);
- hitStuff->addChild(hitMaterial[i] = new SoMaterial);
- hitStuff->addChild(hitTransform[i] = new SoTransform);
- hitStuff->addChild(hitGeometry);
-
- hitTransform[i]->translation.setValue(sx, sy, sz);
- hitTransform[i]->scaleFactor.setValue(SHIELDSIZE * sdx,
- SHIELDSIZE * sdy,
- SHIELDSIZE * sdz);
- hitMaterial[i]->diffuseColor.setIgnored(TRUE);
- hitMaterial[i]->transparency.insertSpace(0, HITPOINTS);
- }
-
- return hitsAll;
- }
-